#pragma once

/*!
 * \file Typelist.h
 *
 * \brief Typelist definition and manipulation
 *
 * Typelist definition and manipulation
 *
 * \author Jorge Barrio Alfonso
 */

#include "FLOW_core/namespace.h"
#include "FLOW_core/null_type.h"

namespace CORE
{
	/*
	 * \brief Basic node of a typelist
	 *
	 * Basic node of a typelist
	 */
	template< typename Type, typename Next >
	struct Typelist
	{
		typedef Type Head;
		typedef Next Tail;
	};

	//Set of macros to define typelist of any length

#define TYPELIST_1( T1 )	\
	Typelist< T1, NullType >
#define TYPELIST_2( T1, T2 )	\
	Typelist< T1, TYPELIST_1( T2 ) >
#define TYPELIST_3( T1, T2, T3 )	\
	Typelist< T1, TYPELIST_2( T2, T3 ) >
#define TYPELIST_4( T1, T2, T3, T4 ) \
	Typelist< T1, TYPELIST_3( T2, T3, T4 ) >
#define TYPELIST_5( T1, T2, T3, T4, T5 )	\
	Typelist< T1, TYPELIST_4( T2, T3, T4, T5 ) >
#define TYPELIST_6( T1, T2, T3, T4, T5, T6 )	\
	Typelist< T1, TYPELIST_5( T2, T3, T4, T5, T6 ) >
#define TYPELIST_7( T1, T2, T3, T4, T5, T6, T7 )	\
	Typelist< T1, TYPELIST_6( T2, T3, T4, T5, T6, T7 ) >
#define TYPELIST_8( T1, T2, T3, T4, T5, T6, T7, T8 )	\
	Typelist< T1, TYPELIST_7( T2, T3, T4, T5, T6, T7, T8 ) >
#define TYPELIST_9( T1, T2, T3, T4, T5, T6, T7, T8, T9 )	\
	Typelist< T1, TYPELIST_8( T2, T3, T4, T5, T6, T7, T8, T9 ) >
#define TYPELIST_10( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 )	\
	Typelist< T1, TYPELIST_9( T2, T3, T4, T5, T6, T7, T8, T9, T10 ) >
#define TYPELIST_11( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 )	\
	Typelist< T1, TYPELIST_10( T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 ) >
#define TYPELIST_12( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 )	\
	Typelist< T1, TYPELIST_11( T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 ) >
#define TYPELIST_13( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 )	\
	Typelist< T1, TYPELIST_12( T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 ) >
#define TYPELIST_14( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 )	\
	Typelist< T1, TYPELIST_13( T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 ) >
#define TYPELIST_15( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 )	\
	Typelist< T1, TYPELIST_14( T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 ) >
#define TYPELIST_16( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16 )	\
	Typelist< T1, TYPELIST_15( T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16 ) >
#define TYPELIST_17( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17 )	\
	Typelist< T1, TYPELIST_16( T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17 ) >
#define TYPELIST_18( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18 )	\
	Typelist< T1, TYPELIST_17( T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18 ) >
#define TYPELIST_19( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19 )	\
	Typelist< T1, TYPELIST_18( T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19 ) >
#define TYPELIST_20( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20 )	\
	Typelist< T1, TYPELIST_19( T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20 ) >
#define TYPELIST_21( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21 )	\
	Typelist< T1, TYPELIST_20( T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21 ) >
#define TYPELIST_22( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22 )	\
	Typelist< T1, TYPELIST_21( T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22 ) >
#define TYPELIST_23( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23 )	\
	Typelist< T1, TYPELIST_22( T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23 ) >
#define TYPELIST_24( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24 )	\
	Typelist< T1, TYPELIST_23( T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24 ) >
#define TYPELIST_25( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25 )	\
	Typelist< T1, TYPELIST_24( T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25 ) >
#define TYPELIST_26( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26 )	\
	Typelist< T1, TYPELIST_25( T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26 ) >
#define TYPELIST_27( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27 )	\
	Typelist< T1, TYPELIST_26( T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27 ) >
#define TYPELIST_28( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28 )	\
	Typelist< T1, TYPELIST_27( T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28 ) >
#define TYPELIST_29( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29 )	\
	Typelist< T1, TYPELIST_28( T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29 ) >
#define TYPELIST_30( T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30 )	\
	Typelist< T1, TYPELIST_29( T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30 ) >


	/*!
	 * \brief struct to calculate length of a typelist in compile time
	 *
	 * struct to calculate length of a typelist in compile time
	 */
	template< typename TList > struct TLLength;

	template< >
	struct TLLength< NullType >
	{
		enum 
		{
			value = 0;
		};
	};

	template< typename T, typename U >
	struct TLLength< Typelist< T, U > >
	{
		enum
		{
			value = 1 + TLLength< U >::value;
		};
	};

	/*!
	 * \brief Returns type in a typelist at specified position
	 *
	 * Returns type in a typelist at specified position
	 */
	template< typename TList, unsigned int i > 
	struct TLAt;

	template< typename Head, typename Tail >
	struct TLAt< Typelist< Head, Tail >, 0 >
	{
		typedef Head Result;
	};

	template< typename Head, typename Tail, unsigned int i >
	struct TLAt< Typelist< Head, Tail >, i >
	{
		typedef typename TLAt< Tail, i - 1 >::Result Result;
	};

	/*!
	 * \brief Returns index of a specified type in a typelist
	 *
	 * Returns index of a specified type in a typelist. Returns -1 if the type is not in the typelist
	 */
	template< typename TList, typename T >
	struct TLIndexOf;

	template< typename T >
	struct TLIndexOf< NullType, T >
	{
		enum
		{
			value = -1;
		};
	};

	template< typename T, typename Tail >
	struct TLIndexOf< TypeList< T, Tail >, T >
	{
		enum
		{
			value = 0;
		};
	};

	template< typename Head, typename Tail, typename T >
	struct TLIndexOf< TypeList< Head, Tail >, T >
	{
	private:
		enum
		{
			temp = IndexOf< Tail, T >::value;
		};
	public:
		enum
		{
			value = temp == -1 ? -1 : 1 + temp;
		};
	};
};

